/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = ora_charset.c                                      */
/*  NOTE      =                                                    */
/*  DATE      = 2015/03/12 by songbb                               */
/*******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <iconv2utf8.h>

#include "frmem.h"
#include "frlog.h"
#include "frsutl.h"

#include "ora_util.h"
#include "ora_charset.h"

typedef struct __st_ora_charset_map
{
    unsigned int ui_code;
    char arr_name[32];
} Ora_charset_map;

/**
* 字符集对照表（jdbc驱动源码中的编码表对应到iconv帮助中的编码表，不完全）
*/
Ora_charset_map arr_charset_maps[] = {
    {1, "ASCII"},
    {2, "DEC"},
    {5, "IBM037"},
    {6, "IBM500"},
    {7, "IBM1140"},
    {8, "IBM285"},
    {9, "IBM1146"},
    {10, "CSPC850MULTILINGUAL"},
    {27, "IBM1148"},
    {31, "ISO-8859-1"},
    {32, "ISO-8859-2"},
    {33, "ISO-8859-3"},
    {34, "ISO-8859-4"},
    {35, "ISO-8859-5"},
    {36, "ISO-8859-6"},
    {37, "ISO-8859-7"},
    {38, "ISO-8859-8"},
    {39, "ISO-8859-9"},
    {40, "ISO-8859-10"},
    {45, "WINDOWS-1258"},
    {46, "ISO-8859-15"},
    {47, "ISO-8859-13"},
    {48, "ISO-8859-14"},
    {52, "ISO-8859-9"},
    {90, "IBM037"},
    {91, "IBM500"},
    {92, "IBM424"},
    {93, "IBM1026"},
    {94, "IBM871"},
    {95, "IBM284"},
    {96, "IBM1047"},
    {97, "IBM1140"},
    {98, "IBM1145"},
    {99, "IBM1148"},
    {100, "IBM1047"},
    {170, "WINDOWS-1250"},
    {171, "WINDOWS-1251"},
    {174, "WINDOWS-1253"},
    {175, "WINDOWS-1255"},
    {177, "WINDOWS-1254"},
    {178, "WINDOWS-1252"},
    {179, "WINDOWS-1257"},
    {180, "IBM273"},
    {181, "IBM280"},
    {182, "IBM277"},
    {183, "IBM278"},
    {184, "IBM870"},
    {185, "IBM1025"},
    {186, "IBM297"},
    {188, "IBM1025"},
    {189, "IBM1141"},
    {194, "IBM1112"},
    {197, "CSPC775BALTIC"},
    {198, "IBM1142"},
    {199, "IBM1143"},
    {200, "IBM1144"},
    {208, "IBM1147"},
    {301, "IBM870"},
    {311, "IBM875"},
    {312, "IBM1026"},
    {314, "IBM1112"},
    {315, "IBM424"},
    {316, "IBM870"},
    {317, "IBM1025"},
    {322, "IBM1025"},
    {323, "IBM1025"},
    {324, "IBM875"},
    {325, "IBM1158"},
    {326, "IBM1158"},
    {327, "IBM423"},
    {320, "IBM420"},
    {381, "IBM875"},
    {560, "WINDOWS-1256"},
    {590, "ISO6937"},
    {835, "IBM930"},
    {850, "GB2312"},
    {851, "GB2312"},
    {852, "GBK"},
    {854, "GB18030"},
    {860, "EUCCN"},
    {865, "BIG5"},
    {868, "BIG5HKSCS"},
    {871, "UTF8"},
    {873, "UTF8"},
    {2000, "UTF16"},
    {2002, "UTF16LE"}
};

/*******************************************************************************
*  FUNC     :  用二分法查找charset maps
*  ARGS     :  ui_key                               (charset编号)
*  RTN      :  -1                                   (未找到)
*              index                                (找到的下标)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __binary_search(unsigned int ui_key)
{
    int len = sizeof(arr_charset_maps) / sizeof(arr_charset_maps[0]);
    int mid = 0;
    int front = 0;
    int back = len - 1;
    while (front <= back)
    {
        mid = (front + back) / 2;
        if (arr_charset_maps[mid].ui_code == ui_key)
            return mid;
        if (arr_charset_maps[mid].ui_code < ui_key)
            front = mid + 1;
        else
            back = mid - 1;
    }
    return -1;
}

/*******************************************************************************
*  FUNC     :  字符串转UTF8
*  ARGS     :  s_charset                            (charset编号)
*           :  *p_buff_in                           (输入的buffer)
*           :  ui_buff_in_len                       (输入的buffer长度)
*           :  **pp_buff_out                        (输出的buffer)
*           :  *p_ui_buff_out_len                   (输出的buffer长度)
*  RTN      :  0                                    (FR_SUCC)
*              1                                    (FR_FAIL)
*  NOTE     :
*******************************************************************************/
int ora_str_to_utf8(L7session_ora *p_st_sess
                    , short s_charset
                    , char *p_buff_in
                    , unsigned int ui_buff_in_len
                    , char **pp_buff_out
                    , unsigned int *p_ui_buff_out_len)
{
    char *p_buff_out = NULL;
    int i_charset_map_index = 0;
    int i_iconv_ret = 0;

    if ((i_charset_map_index = __binary_search(s_charset)) != -1)
    {
        if ((i_iconv_ret = iconv_to_utf8(p_buff_in, ui_buff_in_len, arr_charset_maps[i_charset_map_index].arr_name, &p_buff_out, p_ui_buff_out_len)) == FR_SUCC)
            goto success;
        ora_free((void **) &p_buff_out);

        if (i_iconv_ret == 1 && p_st_sess->i_charset_assumed == FR_YES)
        {
            if (p_st_sess->i_use_oci == FR_NO && s_charset == 871)
                s_charset = 852;
            else if (p_st_sess->i_use_oci == FR_YES && s_charset == 852)
                s_charset = 871;

            if ((i_charset_map_index = __binary_search(s_charset)) != -1)
            {
                if (iconv_to_utf8(p_buff_in, ui_buff_in_len, arr_charset_maps[i_charset_map_index].arr_name, &p_buff_out, p_ui_buff_out_len) == FR_SUCC)
                {
                    ORA_PRINT_WARN("reset s_cli_charset_out from %d to %d\n", p_st_sess->s_cli_charset_out, s_charset);
                    p_st_sess->s_cli_charset_out = s_charset;
                    goto success;
                }
                ora_free((void **) &p_buff_out);
            }
        }
        else
            ORA_PRINT_CRIT("iconv failed\n");
    }

    if ((p_buff_out = (char *) frmalloc((ui_buff_in_len + 1) * sizeof(char))) == NULL)
    {
        ORA_MALLOCK_ERR;
        goto error;
    }
    memcpy(p_buff_out, p_buff_in, ui_buff_in_len);
    p_buff_out[ui_buff_in_len] = '\0';
    *p_ui_buff_out_len = ui_buff_in_len;

success:
    *pp_buff_out = p_buff_out;
    return FR_SUCC;
error:
    ora_free((void **) &p_buff_out);
    return FR_FAIL;
}
